﻿using IGeekFan.AspNetCore.Knife4jUI;
using In66.Net.Elasticsearch;
using In66.Net.Mapper.Extensions;
using In66.Net.Repository;
using In66.Net.Share.Application.Interceptor.OperateLog;
using In66.Net.Share.Application.Tracker;
using In66.Net.Share.Models.OrmType;
using Swashbuckle.AspNetCore.Swagger;
using Yitter.IdGenerator;
using static System.Net.Mime.MediaTypeNames;

namespace In66.Net.Share.Registrar
{
    public abstract partial class AbstractWebApiDependencyRegistrar : IWebApiRegistrar, IApplicationRegistrar, IMiddlewareRegistrar
    {
        public string Name => "webapi";
        public IConfiguration Configuration { get; init; }
        public IServiceCollection Services { get; init; }
        public IServiceInfo ServiceInfo { get; init; }
        public List<AddressNode> RpcAddressInfo { get; init; }
        public IConfigurationSection RedisSection { get; init; }
        public IConfigurationSection MysqlSection { get; init; }
        public IConfigurationSection MongoDbSection { get; init; }
        public IConfigurationSection ConsulSection { get; init; }
        public IConfigurationSection KestrelSection { get; init; }
        public IConfigurationSection RabbitMqSection { get; init; }
        public IConfigurationSection ElasticsearchSection { get; init; }

        public readonly IApplicationBuilder App;

        /// <summary>
        /// 服务注册与系统配置
        /// </summary>
        /// <param name="services"></param>
        public AbstractWebApiDependencyRegistrar(IServiceCollection services)
        {
            Services = services ?? throw new ArgumentException("IServiceCollection is null.");
            Configuration = services.GetConfiguration() ?? throw new ArgumentException("Configuration is null.");
            ServiceInfo = services.GetServiceInfo() ?? throw new ArgumentException("ServiceInfo is null.");
            RedisSection = Configuration.GetSection(RedisConfig.Name);
            MongoDbSection = Configuration.GetSection(MongoConfig.Name);
            MysqlSection = Configuration.GetSection(MysqlConfig.Name);
            RpcAddressInfo = Configuration.GetSection(AddressNode.Name).Get<List<AddressNode>>();
            ConsulSection = Configuration.GetSection(ConsulConfig.Name);
            KestrelSection = Configuration.GetSection(KestrelConfig.Name);
            RabbitMqSection = Configuration.GetSection(RabbitMqConfig.Name);
            ElasticsearchSection = Configuration.GetSection(ElasticsearchConfig.Name);
        }

        /// <summary>
        /// 中间件注册方法
        /// </summary>
        /// <param name="app"></param>
        public AbstractWebApiDependencyRegistrar(IApplicationBuilder app)
        {
            App = app;
        }

        #region 注册服务Web入口方法
        /// <summary>
        /// 注册服务Web入口方法
        /// </summary>
        public abstract void AddCoreWebApi();

        /// <summary>
        /// 注册Webapi通用的服务
        /// </summary>
        /// <typeparam name="THandler"></typeparam>
        protected virtual void AddWebApiDefault() => AddWebApiDefault<BearerAuthenticationRemoteProcessor>();

        /// <summary>
        /// 注册Webapi通用的服务
        /// </summary>
        /// <typeparam name="TAuthenticationProcessor"><see cref="AbstractAuthenticationProcessor"/></typeparam>
        /// <typeparam name="TAuthorizationHandler"><see cref="AbstractPermissionHandler"/></typeparam>
        protected virtual void AddWebApiDefault<TAuthenticationProcessor>()
             where TAuthenticationProcessor : AbstractAuthenticationProcessor
        {
            Services
            .AddInjection()
            .AddHttpContextAccessor()
            .AddMemoryCache()
            .AddCors(ServiceInfo)
            .AddAuthentication<TAuthenticationProcessor>(Configuration)
            .ConfigureConfig(Configuration, ServiceInfo)
            .AddControllers(Configuration, ServiceInfo)
            .AddSwagger(ServiceInfo)
            .AddRouting(options => options.LowercaseUrls = true);
        }
        #endregion

        #region 注册服务Application入口方法
        /// <summary>
        /// 注册服务Application入口方法
        /// </summary>
        public abstract void AddCoreApplication();

        /// <summary>
        /// 注册application通用服务
        /// </summary>
        protected virtual void AddApplicaitonDefault(OrmType ormType = OrmType.SqlSugar)
        {
            AddHostedServices();
            AddCaching();
            AddBloomFilters();

            #region SqlSugar

            if (OrmType.SqlSugar == ormType)
            {
                AddContextSugarWithRepositories();
            }

            #endregion

            #region EF

            if (OrmType.EF == ormType)
            {
                AddContextWithRepositories();
            }

            #endregion

            #region Mongo

            AddMongoContextWithRepositries();

            #endregion

            Services.AddInfraAutoMapper(ServiceInfo.StartAssembly);
            Services.AddLog();
            Services.AddLogDashboard(opt =>
            {
                opt.AddAuthorizationFilter(new LogDashboard.Authorization.Filters.LogDashboardBasicAuthFilter("admin", "123"));
            });

            var consulOption = ConsulSection.Get<ConsulConfig>();
            if (consulOption.Enabled)
            {
                Services.AddInfraConsul(ConsulSection, KestrelSection);
            }

            Services.AddSingleton(typeof(Lazy<>));
            Services.AddScoped<AuthenticationInfo>();

            Services.AddScoped<OperateLogInterceptor>();
            Services.AddScoped<OperateLogAsyncInterceptor>();
            Services.AddSingleton<IBloomFilter, NullBloomFilter>();
            Services.AddSingleton<BloomFilterFactory>();
            Services.AddScoped<IMessageTracker, DbMessageTrackerService>();
            //Services.AddScoped<IMessageTracker, RedisMessageTrackerService>();
            Services.AddScoped<MessageTrackerFactory>();

            #region 雪花漂移算法
            // 雪花漂移算法
            // 创建 IdGeneratorOptions 对象，请在构造函数中输入 WorkerId：
            var options = new IdGeneratorOptions(1);
            // 保存参数（必须的操作，否则以上设置都不能生效）：
            YitIdHelper.SetIdGenerator(options);
            #endregion
        }

        #endregion

        #region 中间件入口方法
        /// <summary>
        /// 注册中间件入口方法
        /// </summary>
        /// <param name="app"></param>
        public abstract void UseCore();

        /// <summary>
        /// 注册webapi通用中间件
        /// </summary>
        protected virtual void UseWebApiDefault(
            Action<IApplicationBuilder> beforeAuthentication = null,
            Action<IApplicationBuilder> afterAuthentication = null,
            Action<IApplicationBuilder> afterAuthorization = null,
            Action<IEndpointRouteBuilder> endpointRoute = null)
        {
            ServiceLocator.Provider = App.ApplicationServices;
            var environment = App.ApplicationServices.GetService<IHostEnvironment>();
            var serviceInfo = App.ApplicationServices.GetService<IServiceInfo>();
            var defaultFilesOptions = new DefaultFilesOptions();
            defaultFilesOptions.DefaultFileNames.Clear();

            App.UseLogDashboard();
            App.UseCustomException();
            //App.UseRequestResponseLog();
            //App.UseIpLogMildd();
            App.UseDefaultFiles(defaultFilesOptions)
                .UseStaticFiles()
                .UseCors(serviceInfo.CorsPolicy);

            #region Nswag

            //App.UseOpenApi()
            //   .UseSwaggerUi3(options =>
            //   {
            //       options.SetSpanEditable();

            //   }).UseRouting();

            #endregion

            #region Swagger

            //设置前缀已易于ocelot swagger文档的集成,ServiceName.Replace(".api", "")这个系统服务名需要优化
            var prefix = serviceInfo.ServiceName.Replace(".api", "");

            App.UseSwagger(options =>
            {
                options.RouteTemplate = prefix + "/swagger/{documentName}/swagger.json";
            });

            var apiVersionDescriptionProvider = InternalApp.ServiceProvider.GetRequiredService<IApiVersionDescriptionProvider>();

            App.UseSwaggerUI(options =>
            {
                options.SetSpanEditable();

                foreach (var description in apiVersionDescriptionProvider.ApiVersionDescriptions)
                {
                    //配置swagger RoutePrefix 方便 自定义页面的配置
                    options.RoutePrefix = $"{prefix}/swagger";

                    options.SwaggerEndpoint($"/{prefix}/swagger/{description.GroupName}/swagger.json", description.GroupName.ToUpperInvariant());
                }
            });

            App.UseKnife4UI(options =>
            {
                options.RoutePrefix = "";

                foreach (var description in apiVersionDescriptionProvider.ApiVersionDescriptions)
                {
                    options.SwaggerEndpoint($"/{prefix}/swagger/{description.GroupName}/swagger.json", description.GroupName.ToUpperInvariant());
                }
            });


            foreach (var description in apiVersionDescriptionProvider.ApiVersionDescriptions)
            {
                App.UseReDoc(options =>
                {
                    options.DocumentTitle = $"API Documentation {description.GroupName}";
                    options.SpecUrl = $"/{prefix}/swagger/{description.GroupName}/swagger.json";
                    options.RoutePrefix = $"api-docs-{description.GroupName}";
                });
            }

            #endregion

            App.UseRouting();
            beforeAuthentication?.Invoke(App);
            App.UseAuthentication();
            afterAuthentication?.Invoke(App);
            App.UseAuthorization();
            afterAuthorization?.Invoke(App);
            App.UseEndpoints(endpoints =>
                {
                    endpointRoute?.Invoke(endpoints);
                    endpoints.MapControllers().RequireAuthorization();
                });
        }

        #endregion
    }
}
